#pragma once

#include "muduo/net/TcpServer.h"
#include "muduo/base/Logging.h"

using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;

// RFC 862
class EchoServer
{
 public:
  EchoServer(muduo::net::EventLoop* loop,
             const muduo::net::InetAddress& listenAddr);

  void start();  // calls server_.start();

 private:
  void onConnection(const muduo::net::TcpConnectionPtr& conn);

  void onMessage(const muduo::net::TcpConnectionPtr& conn,
                 muduo::net::Buffer* buf,
                 muduo::Timestamp time);

  muduo::net::TcpServer server_;
};

// using namespace muduo;
// using namespace muduo::net;

EchoServer::EchoServer(muduo::net::EventLoop *loop,
                       const muduo::net::InetAddress &listenAddr)
    : server_(loop, listenAddr, "EchoServer") {
  // 在构造函数里注册回调函数
  server_.setConnectionCallback(
      std::bind(&EchoServer::onConnection, this, _1));
  server_.setMessageCallback(
      std::bind(&EchoServer::onMessage, this, _1, _2, _3));
}

void EchoServer::start() { server_.start(); }

// onConnection, onMessage 这两个函数体现了“基于事件编程”的典型做法，即程序主体是被动等待事件发生，事件发生之后网络库会调用（回调）事先注册的事件处理函数(event handler)。

// 在`onConnection()`函数中，`conn`参数是`TcpConnection`对象的`shared_ptr`，`TcpConnection::connected()`返回一个`bool`值，表明目前连接是建立还是断开，
// `TcpConnection`的`peerAddress()`和`localAddress()`成员函数分别返回对方和本地的地址（以`InetAddress`对象表示的`IP`和`port`）
void EchoServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
{
  LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> "
           << conn->localAddress().toIpPort() << " is "
           << (conn->connected() ? "UP" : "DOWN");
}

// CH07
// 这段代码实现的不是行回显(line echo)服务，而是有一点数据就发送一点数据。这样可以避免客户端恶意地不发送换行字符，而服务端又必须缓存已经收到的数据，导致服务器内存暴涨。
// 但这个程序还是有一个安全漏洞，即如果客户端故意不断发送数据，但从不接收，那么服务端的发送缓冲区会一直堆积，导致内存暴涨。
// 解决办法可以参考下面的chargen协议，或者在发送缓冲区累积到一定大小时主动断开连接。一般来说，非阻塞网络编程中正确处理数据发送比接收数据要困难，因为要应对对方接收缓慢的情况

// CH06
// 在`onMessage()`函数中，
// - `conn`参数是收到数据的那个TCP连接；
// - `buf`是已经收到的数据，buf的数据会累积，直到用户从中取走(retrieve)数据。注意`buf`是指针，表明用户代码可以修改（消费）`buffer`；
// - `time`是收到数据的确切时间，即`epoll_wait(2)`返回的时间，注意这个时间通常比`read(2)`发生的时间略早，可以用于正确测量程序的消息处理延迟。
//    另外，`Timestamp`对象采用pass-by-value，而不是pass-by-(const)reference，这是有意的，因为在x86-64上可以直接通过寄存器传参。
void EchoServer::onMessage(const muduo::net::TcpConnectionPtr &conn,
                           muduo::net::Buffer *buf, muduo::Timestamp time) {
  // 函数体内: echo服务的“业务逻辑”​：把收到的数据原封不动地发回客户端。
  muduo::string msg(buf->retrieveAllAsString());
  LOG_INFO << conn->name() << " echo " << msg.size() << " bytes, "
           << "data received at " << time.toString();
  conn->send(msg); // 注意我们不用`send(msg)`是否完整地发送了数据，因为muduo网络库会帮我们管理发送缓冲区。
}

